package com.example.demospringboot;

import com.example.demospringboot.flow.converter.CustomBpmnJsonConverter;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.common.engine.impl.util.ReflectUtil;
import org.flowable.editor.language.json.converter.BpmnJsonConverter;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.flowable.engine.runtime.ProcessInstance;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.InputStream;
import java.util.*;

/**
 * @author zhaoPin
 * @Date 2024/8/16  17:16
 * @description: 解析自定义流程属性，通过在xml文件中增加 代码文件来实现 ，这里以 xml 文件举例
 */
@SpringBootTest
public class Flowable08Attribute {

    @Autowired
    RepositoryService repositoryService;

    @Autowired
    RuntimeService runtimeService;

    /**
     * 解析自定义流程属性，包含 字段属性、数组、对象
     */
    @Test
    public void parallelDeployFlow() throws XMLStreamException {

        /**
         *  这里仅以xml文件展示，json 文件参照下面的 方法
         * org.flowable.editor.language.json.converter.BpmnJsonConverter#convertToBpmnModel(com.fasterxml.jackson.databind.JsonNode)
         */

        String resource = "process/08-自定义属性/user-defined-attribute-demo01.bpmn20.xml";

        XMLInputFactory xif = XMLInputFactory.newInstance();
        InputStream in = ReflectUtil.getResourceAsStream(resource);
        XMLStreamReader xtr = xif.createXMLStreamReader(in);
        BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);

        // 为了方便观察，xml 流程中仅配置了一个用户节点，所以这里采用的 get(0)
        final Process process = bpmnModel.getProcesses().get(0);

        // 获得流程节点的要素
        final Collection<FlowElement> flowElements = process.getFlowElements();

        /**
         * 查看 flowElements  节点
         *  - UserTask
         *  - extensionElements
         *  - attributes
         *
         *  通过 debug 可以观察到 flowable 会自动去除掉 自定义属性前面的 flowable:
         */
        for (FlowElement flowElement : flowElements) {
            // 获得流程的属性
            final Map<String, List<ExtensionAttribute>> attributes = flowElement.getAttributes();
            if (Objects.nonNull(attributes) && attributes.size() > 0) {
                System.out.println(1231);
            }
        }

        System.out.println("bpmnModel.toString() = ");
    }


    /**
     * 流程部署操作-- bpmnModel 格式
     */
    @Test
    void parseXMLBpmnModelFlow() throws Exception {
        String resource = "process/07-back/back-demo2.bpmn20.xml";

        XMLInputFactory xif = XMLInputFactory.newInstance();
        InputStream in = ReflectUtil.getResourceAsStream(resource);
        XMLStreamReader xtr = xif.createXMLStreamReader(in);
        BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);

        // 部署 bpmnModel
        // 将 BpmnModel 对象转换为 BPMN XML 字符串
        byte[] bpmnXmlString = new BpmnXMLConverter().convertToXML(bpmnModel);

        // 部署流程定义
        Deployment deployment = repositoryService.createDeployment()
                .addBytes("back-demo2.bpmn20.xml", bpmnXmlString)
                .deploy();

        // 查询已部署的流程定义
        ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();
        List<ProcessDefinition> deployedProcesses = query.deploymentId(deployment.getId()).list();

        for (ProcessDefinition processDefinition : deployedProcesses) {
            System.out.println("Deployed process definition key: " + processDefinition.getKey());
        }
    }

    /**
     * 将 xml 格式转为 json 格式
     * 注意：这种转换出来的json 会丢失 xml 中的自定义属性
     *
     * @throws Exception
     */
    @Test
    void parseXmlFlowToJsonFlow() throws Exception {

        String resource = "process/08-自定义属性/user-defined-attribute-demo01.bpmn20.xml";

        XMLInputFactory xif = XMLInputFactory.newInstance();
        InputStream in = ReflectUtil.getResourceAsStream(resource);
        XMLStreamReader xtr = xif.createXMLStreamReader(in);
        BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);
        ObjectNode node = new BpmnJsonConverter().convertToJson(bpmnModel);

        BpmnModel bpmnModelTwo = new BpmnJsonConverter().convertToBpmnModel(node);


        System.out.println("*********************");
        System.out.println("*********************");
        System.out.println(node.toString());

        System.out.println("*********************");
        System.out.println("*********************");

    }


    /**
     * 流程部署操作--json 格式  自定义属性
     * <p>
     * json文件部署后，act_re_procdef 表中不会产生对应的流程部署文件信息，在 act_re_deployment 中会有记录 ，产生这个问题的原因是addBytes 的文件名需要以  .bpmn20.xml 为后缀
     */
    @Test
    void deployJsonFlowLocal() throws Exception {

        String resource = "process/08-自定义属性/user-defined-attribute-demo01.json";

        // 转为 bpmnModel
        InputStream jsonStream = this.getClass().getClassLoader().getResourceAsStream(resource);
        JsonNode modelNode = new ObjectMapper().readTree(jsonStream);
        BpmnModel bpmnModel = new CustomBpmnJsonConverter().convertToBpmnModel(modelNode);

        // 将 BpmnModel 对象转换为 BPMN XML 字符串
        byte[] bpmnXmlString = new BpmnXMLConverter().convertToXML(bpmnModel);

       /*  // 部署流程定义
        Deployment deployment = repositoryService.createDeployment()
                .addBytes("user-defined-attribute-demo01.bpmn20.xml", bpmnXmlString)
                .deploy();

        // 查询已部署的流程定义
        ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();
        List<ProcessDefinition> deployedProcesses = query.deploymentId(deployment.getId()).list();

        for (ProcessDefinition processDefinition : deployedProcesses) {
            System.out.println("Deployed process definition key: " + processDefinition.getKey());
        } */
    }


    /**
     * 流程部署操作--json 格式 手动封装 成对象属性
     */
    @Test
    void deployJsonFlow() throws Exception {
        // JSON 字符串
        String resource = "process/08-自定义属性/leave-application.json";
        // 转为 bpmnModel
        InputStream jsonStream = this.getClass().getClassLoader().getResourceAsStream(resource);
        JsonNode rootNode = new ObjectMapper().readTree(jsonStream);

        // 构建 BpmnModel 对象
        BpmnModel bpmnModel = new BpmnModel();
        Process process = new Process();
        process.setId(rootNode.get("processDefinitionKey").asText());

        // 添加 StartEvent
        StartEvent startEvent = new StartEvent();
        startEvent.setId(rootNode.get("startEvent.id").asText());
        startEvent.setName(rootNode.get("startEvent.name").asText());
        process.addFlowElement(startEvent);

        // 添加 UserTasks
        JsonNode tasksNode = rootNode.get("tasks");
        for (JsonNode taskNode : tasksNode) {
            UserTask userTask = new UserTask();
            userTask.setId(taskNode.get("id").asText());
            userTask.setName(taskNode.get("name").asText());
            userTask.setAssignee(taskNode.get("assignee").asText());
            process.addFlowElement(userTask);
        }

        // 添加 ExclusiveGateway
        JsonNode gatewaysNode = rootNode.get("gateways");
        for (JsonNode gatewayNode : gatewaysNode) {
            ExclusiveGateway exclusiveGateway = new ExclusiveGateway();
            exclusiveGateway.setId(gatewayNode.get("id").asText());
            exclusiveGateway.setName(gatewayNode.get("name").asText());


            JsonNode conditionsNode = gatewayNode.get("conditions");
            for (JsonNode conditionNode : conditionsNode) {


              /*   ConditionExpression conditionExpression = new ConditionExpression();
                conditionExpression.setText(conditionNode.get("conditionExpression").asText());
                exclusiveGateway.addCondition(conditionExpression); */


                ExtensionElement extensionElement = new ExtensionElement();
                extensionElement.setElementText(conditionNode.get("conditionExpression").asText());
                exclusiveGateway.addExtensionElement(extensionElement);
            }
            process.addFlowElement(exclusiveGateway);

        }

        // 添加 EndEvent
        EndEvent endEvent = new EndEvent();
        endEvent.setId(rootNode.get("endEvent.id").asText());
        endEvent.setName(rootNode.get("endEvent.name").asText());
        process.addFlowElement(endEvent);

        // 添加 SequenceFlows
        JsonNode sequenceFlowsNode = rootNode.get("sequenceFlows");
        for (JsonNode sequenceFlowNode : sequenceFlowsNode) {
            SequenceFlow sequenceFlow = new SequenceFlow();
            sequenceFlow.setId(sequenceFlowNode.get("id").asText());
            sequenceFlow.setSourceRef(sequenceFlowNode.get("sourceRef").asText());
            sequenceFlow.setTargetRef(sequenceFlowNode.get("targetRef").asText());
            process.addFlowElement(sequenceFlow);
        }

        // 设置 Process 为默认流程
        bpmnModel.addProcess(process);

        // 将 BpmnModel 对象转换为 BPMN XML 字符串
        byte[] bpmnXmlString = new BpmnXMLConverter().convertToXML(bpmnModel);

       /*  // 部署流程定义
        Deployment deployment = repositoryService.createDeployment()
                .addBytes("leave-application.bpmn20.xml", bpmnXmlString)
                .deploy();

        // 查询已部署的流程定义
        ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();
        List<ProcessDefinition> deployedProcesses = query.deploymentId(deployment.getId()).list();

        for (ProcessDefinition processDefinition : deployedProcesses) {
            System.out.println("Deployed process definition key: " + processDefinition.getKey());
        } */
    }


    @Test
    void startFlow() throws InterruptedException {
        // 在流程定义表中动态维护
        String processId = "back-demo2:1:c7628eb0-4a62-11ef-987c-b469218e373e";
        Map<String, Object> map = new HashMap<>();
        // 1.根据流程定义ID启动流程实例
        map.put("users", Arrays.asList("张三24", "李四24"));
        ProcessInstance processInstance = runtimeService.startProcessInstanceById(processId, map);
        System.out.println("processInstance.getId() = " + processInstance.getId());
    }


}
